(ns clj-8583.format
  (:use [clj-8583.binary])
  (:use [clj-8583.crypt])
  (:use [clj-8583.bitmap]))

(defn variable-length-field [length-of-length]
  {:reader
    (fn [decoder input]
      (let [[length-bytes remaining-input] (split-at length-of-length input)
            length (Integer/parseInt (bytes-to-ascii length-bytes))
            [field-bytes remaining-input] (split-at length remaining-input)]
        [(bytes-to-ascii field-bytes) remaining-input]))
   :writer
     (fn [encoder value]
        (str (format (str "%0" length-of-length "d") (count value)) (encoder value)))
  })

(defn- error [field-name error-message data]
  {:errors [(str "(" (name field-name) ") Error: " error-message ". The data: [" data "]")]})

(defn fixed-length-field [length]
  {:reader
    (fn [decoder input]
      (if (>= (count input) length)
        (let [[field-bytes remaining-input] (split-at length input)]
          [(decoder field-bytes) remaining-input])
      [(error "field-name" (str "Less than " length " bytes available") (bytes-to-hex input))]))
   :writer
     (fn [encoder value]
       (encoder value))
        ;(if-not (= (count value) length) (format (str "%" (- length (count value)) "s") ""))))
  })

(defn field-definition
  [field-number
   name
   {reader :reader writer :writer} &
   {:keys [codec] :or {codec {:decoder bytes-to-ascii
                              :encoder identity}}}]
  (let [{:keys [decoder encoder]} codec]
    [field-number
    {:name name
      :reader (partial reader decoder)
      :writer (partial writer encoder)}]))


(defn make-field-definitions [descriptions]
  (let [make-field-definition #(apply field-definition %)]
    (into {} (map make-field-definition descriptions))))



(defn response-message-type
  [message-type]
  (format "%04d" (+ (Integer/parseInt message-type) 10)))

(defn message-bytes-no-mac
  [fields field-defs message-definitions]
  (let [message-type (:message-type fields)
        mess-def ((keyword (:message-type fields)) message-definitions)
        ;common-fields (into (sorted-map-by <) (filter #(contains? mess-def (first %)) field-defs))
        common-fields (sort (filter #(contains? mess-def (first %)) field-defs))
        bitmap-bytes (write-bitmap (concat '(1) (map #(first %) common-fields) '(128)))
        mak-bytes (map #((:writer (second %)) ((:name (second %)) fields)) common-fields)
        mak-b (map #(if (= String (class %)) (.getBytes % "iso8859-1") %) mak-bytes)]
    (prn "mak-bytes" mak-bytes)
    (byte-array (apply concat (.getBytes (:message-type fields)) (byte-array bitmap-bytes) mak-b))))

(defn message-bytes
  [fields field-def message-definitions mac-k]
  (let [message-bytes-without-mac (message-bytes-no-mac fields field-def message-definitions)
        message-bytes (byte-array (concat message-bytes-without-mac (xor-des message-bytes-without-mac mac-k)))
        message-length (.getBytes (format "%04d" (count message-bytes)))]
    (byte-array (concat message-length message-bytes))))


(defn mac-validate
  [fields field-defs message-definitions mac-k]
  (let [message-bytes (message-bytes-no-mac fields field-defs message-definitions)
        mac-val (bytes-to-hex (xor-des message-bytes mac-k))]
    ;(prn "message-bytes-without-mac")
    ;(prn (bytes-to-hex message-bytes))
    (if (= mac-val (:mac fields))
      true
      false)))
